// MineGrid.cpp

#include "MineGrid.h"
#include <time.h>
//#include <SFML/System.hpp>
#include <iterator>
#include <stdlib.h>
#include <iostream>

#define PERCENTAGE_OF_MINES 0.15

/**
 * Constructor.
 */
MineGrid::MineGrid()
{
	resize( Size( 10, 10 ) );
//	clock = new sf::Clock();
}

/**
 * Constructor.
 *
 * @param size the new size value.
 */
MineGrid::MineGrid( const Size& size )
{
	resize( size );
	//clock = new sf::Clock();
}

/**
 * Destructor.
 */
MineGrid::~MineGrid()
{
	//delete clock;
}

/**
 * Resets the grid and clock.
 */
void MineGrid::reset()
{
	const unsigned int rows = gridSize.getHeight();
	const unsigned int cols = gridSize.getWidth();
	const int numberOfMines = int( PERCENTAGE_OF_MINES * cols * rows );

	// Reset flags and state.
	std::vector<column>::iterator currentRow;
	column::iterator currentMine;
	for( currentRow = grid.begin() ; currentRow != grid.end() ; ++currentRow )
		for( currentMine = currentRow->begin() ; currentMine != currentRow->end() ; ++currentMine )
			currentMine->resetFlagsAndStates();

	srand( (unsigned int) time( NULL ) );

	// Place the mines
	for( int i = 0 ; i < numberOfMines ; ++i )
	{
		bool notPlaced = true;

		while( notPlaced )
		{
			int x = rand() % cols;
			int y = rand() % rows;

			Mine &mine = grid.at( y ).at( x );

			// Make sure that the point isn't a mine.
			if( !mine.isMine() )
			{
				mine.setAsMine( true );
				notPlaced = false;
				incrementAdjacentSquares( Point( x, y ) );
			}
		}
	}
}

/**
 * Resizes the grid to the given size.
 *
 * @param size the new size of the grid.
 */
void MineGrid::resize( const Size& size )
{
	gridSize = size;

	// Resize the number of rows...
	grid.resize( size.getHeight() );

	// Resize the number of columns in each row...
	std::vector<column>::iterator itGrid;
	for( itGrid = grid.begin() ; itGrid != grid.end() ; ++itGrid )
		itGrid->resize( size.getWidth() );

	// Reset the grid
	reset();
}

/**
 * Checks if the player is done with the game
 *
 * @return bool true if the grid is finished.
 */
bool MineGrid::isDone() const
{
	std::vector<column>::const_iterator currentRow;
	column::const_iterator currentMine;
	for( currentRow = grid.begin() ; currentRow != grid.end() ; ++currentRow )
		for( currentMine = currentRow->begin() ; currentMine != currentRow->end() ; ++currentMine )
			// We aren't done if we find a non-mine that hasn't been swept
			if( !currentMine->isMine() && !currentMine->isSwept() )
				return false;

	return true;
}

/**
 * Checks if the mine has been swept.
 *
 * @param p the point to the mine.
 * @return bool if the mine has been swept.
 */
bool MineGrid::isSwept( const Point& p ) const
{
	if( isValidPoint( p ) )
		return grid.at( p.getY() ).at( p.getX() ).isSwept();

	return false;
}

/**
 * Sweeps the mine.
 *
 * @param p the point to the mine.
 */
void MineGrid::sweepMine( const Point& p )
{
	// Make sure that the point is valid!
	if( !isValidPoint( p ) )
		return;

	Mine &mine = grid.at( p.getY() ).at( p.getX() );

	if( !mine.isSwept() )
	{
		mine.sweep();

		// We found a "0" square. Sweep all of the non-mine squares
		// adjacent to this one.
		if( mine.getAdjacentNumber() == 0 && !mine.isMine() )
			sweepAdjacentNoneMines( p );
	}
	else if( !mine.isFlagged() )
	{
		sweepAdjacentNonFlaggedMines( p );
	}
}

/**
 * Checks if the mine has been flagged.
 *
 * @param p the point to the mine.
 * @return bool if the mine has been flagged.
 */
bool MineGrid::isFlagged( const Point& p ) const
{
	if( isValidPoint( p ) )
		return grid.at( p.getY() ).at( p.getX() ).isFlagged();

	return false;
}

/**
 * Flags the mine.
 *
 * @param p the point to the mine.
 */
void MineGrid::flagMine( const Point& p )
{
	if( isValidPoint( p ) )
		grid.at( p.getY() ).at( p.getX() ).flag();
}

/**
 * Checks wether or not the square is a mine or not.
 *
 * @param p the point to the mine.
 * @return bool if the square is a mine or not.
 */
bool MineGrid::isMine( const Point& p ) const
{
	if( isValidPoint( p ) )
		return grid.at( p.getY() ).at( p.getX() ).isMine();

	return false;
}

/**
* Returns the number of adjacent mines around
* the square around the given mine.
*
* @param p the point to the mine.
* @return int the of adjacent mines.
*/
int MineGrid::getAdjacentMines( const Point& p ) const
{
	return grid.at( p.getY() ).at( p.getX() ).getAdjacentNumber();
}

/**
 * Returns the size of the grid.
 *
 * @return Size the grid size.
 */
Size MineGrid::getSize() const
{
	return gridSize;
}

/**
* Increments the number of adjacent in each of the
* squares around the given point.
*
* @param p the center point.
*/
void MineGrid::incrementAdjacentSquares( const Point& p )
{
	const int r = p.getY();
	const int c = p.getX();

	Point t;

	// Roof and floor squares
	for( int i = 0 ; i < 2 ; ++i )
	{
		t.setY( r - 1 + i * 2 );

		for( int j = -1 ; j <= 1 ; ++j )
		{
			t.setX( c - j );
			if( isValidPoint( t ) )
				grid.at( t.getY() ).at( t.getX() ).incrementAdjacentNumber();
		}
	}
	t.setY( r );

	// Side squares
	t.setX( c - 1 );
	if( isValidPoint( t ) )
		grid.at( r ).at( t.getX() ).incrementAdjacentNumber();

	t.setX( c + 1 );
	if( isValidPoint( t ) )
		grid.at( r ).at( t.getX() ).incrementAdjacentNumber();
}

/**
* Returns the number of adjacent flagged mines.
*
* @param p the center mine.
* @return int the number of adjacent flagged mines.
*/
int MineGrid::numberOfAdjacentFlaggedMines( const Point& p )
{
	const int r = p.getY();
	const int c = p.getX();

	int result = 0;

	Point t;

	// Roof and floor squares
	for( int i = 0 ; i < 2 ; ++i )
	{
		t.setY( r - 1 + i * 2 );

		for( int j = -1 ; j <= 1 ; ++j )
		{
			t.setX( c - j );
			if( isValidPoint( t ) && grid.at( t.getY() ).at( t.getX() ).isFlagged() )
				++result;
		}
	}
	t.setY( r );

	// Side squares
	t.setX( c - 1 );
	if( isValidPoint( t ) && grid.at( r ).at( t.getX() ).isFlagged() )
		++result;

	t.setX( c + 1 );
	if( isValidPoint( t ) && grid.at( r ).at( t.getX() ).isFlagged() )
		++result;

	return result;
}

/**
* Sweeps the adjacent non flagged mines, if the number
* of flagged mines are equal to the adjacency number.
*
* @param p the center mine.
*/
void MineGrid::sweepAdjacentNonFlaggedMines( const Point& p )
{
	if( !isValidPoint( p ) )
		return;

	Mine &mine = grid.at( p.getY() ).at( p.getX() );

	if( mine.getAdjacentNumber() == numberOfAdjacentFlaggedMines( p ) )
		sweepAdjacentNoneMines( p );
}

/**
 * Sweeps all of the adjacent non-mine squares.
 *
 * @param p the center mine.
 */
void MineGrid::sweepAdjacentNoneMines( const Point& p )
{
	// The middle point
	const int c = p.getX();
	const int r = p.getY();

	// Temp variables
	Point t;

	// Roof and floor mines
	for( int i = 0 ; i < 2 ; ++i )
	{
		t.setY( r - 1 + i * 2 );

		for( int j = -1 ; j <= 1 ; ++j )
		{
			t.setX( c - j );
			checkAdjacentNoneMine( t );
		}
	}

	// The sides
	t.setY( r );

	t.setX( c - 1 );
	checkAdjacentNoneMine( t );

	t.setX( c + 1 );
	checkAdjacentNoneMine( t );
}

/**
 * Checks if the point is an "empty/0" square and
 * sweeps the adjacent mines accordingly.
 *
 * @param p position of the center mine to check
 */
void MineGrid::checkAdjacentNoneMine( const Point& p )
{
	if( !isValidPoint( p ) )
		return;

	const int x = p.getX();
	const int y = p.getY();

	Mine &mine = grid.at( y ).at( x );

	// Make sure that the mine isn't swept, flagged and not
	// a mine.
	if( !mine.isSwept() && !mine.isFlagged() && !mine.isMine() )
	{
		mine.sweep();
		Point t( x, y );

		if( mine.getAdjacentNumber() == 0 )
			sweepAdjacentNoneMines( t );
	}
}

/**
 * Checks wether or not the point is a valid position
 * in the grid.
 *
 * @param p the point to validate.
 * @return bool true if the point is valid.
 */
bool MineGrid::isValidPoint( const Point& p ) const
{
	const int x = p.getX();
	const int y = p.getY();

	return ( // Check the x-value
		( 0 <= x && x < gridSize.getWidth() ) &&
		// Check the y-value
		( 0 <= y && y < gridSize.getHeight() ) );
}
